<?php

namespace Xin\Coze\Providers;

use GuzzleHttp\Exception\GuzzleException;
use Psr\SimpleCache\InvalidArgumentException;
use Xin\Coze\SteamIterator;

class ChatProvider extends AbstractProvider
{
    /**
     * 创建对话
     * @param string $botId
     * @param array{
     *     stream?:bool,
     *     user_id?:string,
     *     additional_messages?:array[],
     *     custom_variables?:array<string,string>,
     *     meta_data?:array<string,string>,
     *     extra_params?:array<string,string>,
     *     auto_save_history?:bool,
     *     shortcut_command?:array,
     *     wait?:bool
     * } $data
     * @param array $options
     * @return SteamIterator|array|null
     * @throws GuzzleException
     * @throws InvalidArgumentException
     */
    public function create(string $botId, array $data, array $options = [])
    {
        if (!isset($options['stream'])) {
            if (isset($data['stream'])) {
                $options['stream'] = (bool)$data['stream'];
            } else {
                $options['stream'] = false;
            }
        }

        $options = array_merge([
            'timeout' => 300,
            'wait' => false,
        ], $options);

        // 创建对话
        $data = array_merge([
            'user_id' => '00000',
        ], $data, [
            'bot_id' => $botId,
        ]);
        $result = $this->coze->httpPost('/v3/chat', $data, $options);

        // 非流式返回，则等待对话完成
        if (!$options['stream'] && $options['wait']) {
            return $this->waitMessages($result['conversation_id'], $result['id']);
        }

        return $result;
    }

    /**
     * 创建对话并等待结果
     * @param string $botId
     * @param array{
     *     stream?:bool,
     *     user_id?:string,
     *     additional_messages?:array[],
     *     custom_variables?:array<string,string>,
     *     meta_data?:array<string,string>,
     *     extra_params?:array<string,string>,
     *     auto_save_history?:bool,
     *     shortcut_command?:array,
     * } $data
     * @param array $options
     * @return SteamIterator|array|null
     * @throws GuzzleException
     * @throws InvalidArgumentException
     */
    public function createOrWait(string $botId, array $data, array $options = [])
    {
        $options['wait'] = true;

        return $this->create($botId, $data, $options);
    }

	/**
	 * 获取对话详情
	 * @param string $conversationId
	 * @param string $chatId
	 * @param array $options
	 * @return array
	 */
    public function detail(string $conversationId, string $chatId, array $options = [])
    {
        return $this->coze->httpGet(
            "/v3/chat/retrieve",
            [
                "conversation_id" => $conversationId,
                "chat_id" => $chatId,
            ],
			$options
        );
    }

	/**
	 * 获取消息
	 * @param string $conversationId
	 * @param string $chatId
	 * @param array $options
	 * @return array
	 */
    public function messages(string $conversationId, string $chatId, array $options = [])
    {
        return $this->coze->httpGet(
            "/v3/chat/message/list",
            [
                "conversation_id" => $conversationId,
                "chat_id" => $chatId,
            ],
			$options
        );
    }

    /**
     * 等待消息
     * @param string $conversationId
     * @param string $chatId
     * @param int $maxCount
     * @param array $options
     * @return array{detail:array<string,mixed>,messages:array[]}
     * @throws GuzzleException
     * @throws InvalidArgumentException
     */
    public function waitMessages(string $conversationId, string $chatId, int $maxCount = 200, array $options = [])
    {
        $waitTime = $options['wait_time'] ?? $this->coze->getConfig('wait_time', 1000) * 1000;
        while ($maxCount--) {
            $chatDetail = $this->detail($conversationId, $chatId);
            // $realWaitTime = rand($waitTime / 10, $waitTime);
            $realWaitTime = $waitTime;
            if ($chatDetail['status'] !== 'completed') {
                usleep($realWaitTime);
                continue;
            }

            $messages = $this->messages($conversationId, $chatId);
            return [
                'detail' => $chatDetail,
                'messages' => $messages,
            ];
        }

        return null;
    }

	/**
	 * 取消对话
	 * @param string $conversationId
	 * @param string $chatId
	 * @param array $options
	 * @return array
	 */
    public function cancel(string $conversationId, string $chatId, array $options = [])
    {
        return $this->coze->httpPost(
            "/v3/chat/cancel",
            [
                "conversation_id" => $conversationId,
                "chat_id" => $chatId,
            ],
			$options
        );
    }
}
